﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using Antlr.Runtime;
using SilverlightGraph.Graph;
using SilverlightGraph.Library;
using SilverlightGraph;
using Dot2Silverlight.ServiceReference;

namespace Dot2Silverlight
{
    public partial class Home : Page
    {
        Dictionary<IEntity, EntityControl> buttonRegistry = null;
        IEntity newTarget = null;
        IEntity root = null;
        Graph1 graph;
        List<System.IO.FileInfo> fileColl = new List<System.IO.FileInfo>();
        System.IO.FileInfo _file;
        System.IO.Stream fileStream;
        Dispatcher UIDispatcher;
        string FileSize = ""; //to show while uploading
        int ChunkSize = 4194304;
        long _dataLength;
        long _dataSent = 0;
        string filename = "";
        static Uri baseAddress = Application.Current.Host.Source;
        static Uri address = new Uri(baseAddress, "../ServiceTranslator.svc");
        static Uri httpHandler = new Uri(baseAddress,"../FileUpload.ashx");

        public Home()
        {
            InitializeComponent();
        }

        // Executes when the user navigates to this page.
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
        }

        private void uploadFile_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog fd = new OpenFileDialog();
            fd.Filter = "Plain Files (*.dot)|*.dot";
            fd.Multiselect = false;

            if (fd.ShowDialog() == true)
            {
                foreach (System.IO.FileInfo file in fd.Files)
                {
                    fileColl.Add(file);
                }
            }

            this.UploadFiles();

            uploadFile.IsEnabled = false;
        }

        #region "Upload Files"
        private void UploadFiles()
        {
            foreach (System.IO.FileInfo file in fileColl)
            {
                UIDispatcher = this.Dispatcher;
                FileSize = this.GetFileSize(file.Length);
                this.StartUpload(file);
            }
        }
        #endregion

        #region "Start Upload"
        private void StartUpload(System.IO.FileInfo file)
        {
            _file = file;
            fileStream = _file.OpenRead();
            _dataLength = fileStream.Length;

            long dataToSend = _dataLength - _dataSent;
            bool isLastChunk = dataToSend <= ChunkSize;
            bool isFirstChunk = _dataSent == 0;
            string docType = "document";
            UriBuilder httpHandlerUrlBuilder = new UriBuilder(httpHandler.AbsoluteUri);
            filename = _file.Name;
            httpHandlerUrlBuilder.Query = string.Format("{5}file={0}&offset={1}&last={2}&first={3}&docType={4}", _file.Name, _dataSent, isLastChunk, isFirstChunk, docType, string.IsNullOrEmpty(httpHandlerUrlBuilder.Query) ? "" : httpHandlerUrlBuilder.Query.Remove(0, 1) + "&");

            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(httpHandlerUrlBuilder.Uri);
            webRequest.Method = "POST";
            webRequest.BeginGetRequestStream(new AsyncCallback(WriteToStreamCallback), webRequest);

        }

        #endregion

        #region "Write Stream to Callback"
        private void WriteToStreamCallback(IAsyncResult asynchronousResult)
        {
            HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
            System.IO.Stream requestStream = webRequest.EndGetRequestStream(asynchronousResult);

            byte[] buffer = new Byte[4096];
            int bytesRead = 0;
            int tempTotal = 0;

            //Set the start position
            fileStream.Position = _dataSent;

            //Read the next chunk
            //&& !_file.IsDeleted && _file.State != Constants.FileStates.Error
            while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0 && tempTotal + bytesRead < ChunkSize)
            {
                requestStream.Write(buffer, 0, bytesRead);
                requestStream.Flush();

                _dataSent += bytesRead;
                tempTotal += bytesRead;

                //Show the progress change
                UIDispatcher.BeginInvoke(delegate()
                {
                    UpdateShowProgress(false);
                });

            }

            requestStream.Close();

            //Get the response from the HttpHandler
            webRequest.BeginGetResponse(new AsyncCallback(ReadHttpResponseCallback), webRequest);

        }
        #endregion

        #region "Read HTTP Response"
        private void ReadHttpResponseCallback(IAsyncResult asynchronousResult)
        {

            try
            {
                HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
                HttpWebResponse webResponse = (HttpWebResponse)webRequest.EndGetResponse(asynchronousResult);
                System.IO.StreamReader reader = new System.IO.StreamReader(webResponse.GetResponseStream());

                string responsestring = reader.ReadToEnd();
                reader.Close();
            }
            catch
            {
                UIDispatcher.BeginInvoke(delegate()
                {
                    ShowError();
                });
            }

            if (_dataSent < _dataLength)
            {
                //continue uploading the rest of the file in chunks
                StartUpload(_file);

                //Show the progress change
                UIDispatcher.BeginInvoke(delegate()
                {
                    UpdateShowProgress(false);
                });
            }
            else
            {
                fileStream.Close();
                fileStream.Dispose();

                //Show the progress change
                UIDispatcher.BeginInvoke(delegate()
                {
                    UpdateShowProgress(true);
                });
            }

        }
        #endregion

        #region "Update Show Progress"
        private void UpdateShowProgress(bool complete)
        {
            if (complete)
            {
                //RESET ALL VALUES TO THEY CAN UPLOAD AGAIN
                txtMessage.Text = "complete";
                fileColl.Clear();
                _dataSent = 0;
                _dataLength = 0;
                _file = null;
                UIDispatcher = null;
                fileStream = null;
                FileSize = "";
                uploadFile.IsEnabled = true;

                WebClient client = new WebClient();
                client.OpenReadAsync(new Uri(filename, UriKind.Relative));
                client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadFileCompleted);

                


            }
            else
            {
                txtMessage.Text = "Total file size: " + FileSize + " Uploading: " + string.Format("{0:###.00}%", (double)_dataSent / (double)_dataLength * 100);
                //progressUpload.Value = (double)_dataSent / (double)_dataLength;
            }
        }

        void client_OpenReadFileCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            if (e.Result == null) { MessageBox.Show("Error while retrieving data.", "Error", MessageBoxButton.OK); }
            else
            {
                System.IO.StreamReader myReader = new System.IO.StreamReader(e.Result);
                string content = myReader.ReadToEnd();
                myReader.Close();

                ServiceTranslatorClient client = new ServiceTranslatorClient("CustomBinding_ServiceTranslator", address.AbsoluteUri);
                client.WriteDotFileCompleted += new EventHandler<WriteDotFileCompletedEventArgs>(client_WriteDotFileCompleted);
                client.WriteDotFileAsync(content);
            }
        }

        void client_WriteDotFileCompleted(object sender, WriteDotFileCompletedEventArgs e)
        {
            if (e.Result == null) { MessageBox.Show("Error while retrieving data.", "Error", MessageBoxButton.OK); }
            else
            {
                loadFile(e.Result.ToString());
            }
        }

        void loadFile(string filename)
        {
            WebClient client = new WebClient();
            client.OpenReadAsync(new Uri(filename, UriKind.Relative));
            client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted);
        }

        void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            if (e.Result == null) { MessageBox.Show("Error while retrieving data.", "Error", MessageBoxButton.OK); }
            else
            {
                System.IO.StreamReader myReader = new System.IO.StreamReader(e.Result);
                string content = myReader.ReadToEnd();

                ANTLRStringStream input = new ANTLRStringStream(content);
                DotCsharpLexer lex = new DotCsharpLexer(input);
                CommonTokenStream tokens = new CommonTokenStream(lex);
                DotCsharpParser p = new DotCsharpParser(tokens);
                graph = p.graph();   // launch parser by calling start rule

                if (p.HasError)
                {
                    MessageBox.Show("There were errors during the parsing phase.");
                }
                else
                {
                    try
                    {
                        IEntity entity = this.GetStartingEntity();
                        LoadModel(entity);
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                    }
                }
                myReader.Close();
            }
        }

        IEntity GetStartingEntity()
        {

            NetworkGenerator generator = new NetworkGenerator();
            IEntity entity = generator.CreateGraph(graph);

            root = entity;

            return entity;
        }

        IEntity stopControl; // changed by LoadConnections

        void LoadModel(IEntity entity)
        {
            this.buttonRegistry = new Dictionary<IEntity, EntityControl>();
            this.entityCanvas.Children.Clear();
            this.stopControl = null;

            EntityControl startControl = new EntityControl(entity);
            startControl.Title = entity.Label == null ? entity.Name : entity.Label.Text;
            startControl.Color = Colors.Blue;
            startControl.Width1 = entity.Width;
            startControl.Height1 = entity.Height;
            this.entityCanvas.Children.Add(startControl);

            Canvas.SetLeft(startControl, entity.X - entity.Width / 2);
            Canvas.SetTop(startControl, -entity.Y - entity.Height / 2 + graph.Height);

            this.buttonRegistry.Add(entity, startControl);

            List<IEntity> connections = entity.Connections;
            while (connections.Count > 0)
            {
                connections = this.LoadConnections(connections);
            }
            this.MakeConnections();

            if (this.stopControl != null)
            {
                EntityControl stop = this.buttonRegistry[stopControl];
                stop.Color = Colors.Blue;
            }

            this.mainCanvas.Width = graph.Width;
            this.mainCanvas.Height = graph.Height;// +30;
        }


        List<IEntity> LoadConnections(List<IEntity> ringEntities)
        {
            List<IEntity> nextRing = new List<IEntity>();

            foreach (IEntity entity in ringEntities)
            {
                EntityControl button = new EntityControl(entity);
                button.Title = entity.Label == null ? entity.Name : entity.Label.Text;
                button.Width1 = entity.Width;
                button.Height1 = entity.Height;
                Canvas.SetLeft(button, entity.X - entity.Width / 2);
                Canvas.SetTop(button, -entity.Y - entity.Height / 2 + graph.Height);
                this.entityCanvas.Children.Add(button);
                this.buttonRegistry.Add(entity, button);
                stopControl = entity;
            }

            foreach (IEntity entity in ringEntities)
            {
                foreach (IEntity nextConnection in entity.Connections)
                {
                    if (!this.buttonRegistry.ContainsKey(nextConnection))
                    {
                        if (!nextRing.Contains(nextConnection))
                        {
                            nextRing.Add(nextConnection);
                        }
                    }
                }
            }

            return nextRing;
        }

        void MakeConnections()
        {
            foreach (IEntity entity in this.buttonRegistry.Keys)
            {
                foreach (IEntity connection in entity.Connections)
                {
                    this.EvaluateConnection(entity, connection);
                }
            }
        }

        void EvaluateConnection(IEntity entity, IEntity connection)
        {
            if (this.buttonRegistry.ContainsKey(connection))
            {
                this.DrawConnection(entity, connection);
            }
        }

        void DrawConnection(IEntity entity1, IEntity entity2)
        {
            EntityControl button1 = this.buttonRegistry[entity1];
            EntityControl button2 = this.buttonRegistry[entity2];
            SolidColorBrush blackBrush = new SolidColorBrush();
            blackBrush.Color = Colors.Black;

            foreach (Edge e in graph.Edges)
            {
                if (e.Source.Equals(entity1.Name) && e.Target.Equals(entity2.Name))
                {
                    Pair first = e.Points.First();
                    double x = (double)first.First;
                    double y = (double)first.Second;

                    /* Line */
                    BezierSegment be = new BezierSegment();
                    Path p = new Path();
                    p.Stroke = blackBrush;
                    p.StrokeThickness = 2;

                    PathFigure pf = new PathFigure();
                    pf.IsClosed = false;
                    pf.StartPoint = new Point(x, -y + graph.Height);

                    PolyBezierSegment pbs = new PolyBezierSegment();
                    for (int i = 1; i < e.Points.Count; i++)
                    {
                        Pair point = e.Points[i];
                        x = (double)point.First;
                        y = (double)point.Second;
                        pbs.Points.Add(new Point(x, -y + graph.Height));
                    }
                    pf.Segments.Add(pbs);


                    PathGeometry pg = new PathGeometry();
                    pg.Figures.Add(pf);
                    p.Data = pg;

                    /* Arrow Head */
                    //
                    // Make the triangle
                    //
                    List<Pair> points = e.ArrowPoints;

                    Path path = new Path();

                    path.Fill = new SolidColorBrush(Colors.Black);
                    path.Stroke = new SolidColorBrush(Colors.Black);

                    PathFigure pathFigure = new PathFigure();

                    Pair pair0 = points[0];
                    x = (double)pair0.First;
                    y = (double)pair0.Second;
                    pathFigure.StartPoint = new Point(x, -y + graph.Height);

                    LineSegment lineSegment1 = new LineSegment();
                    Pair pair1 = points[1];
                    x = (double)pair1.First;
                    y = (double)pair1.Second;
                    lineSegment1.Point = new Point(x, -y + graph.Height);
                    pathFigure.Segments.Add(lineSegment1);

                    LineSegment lineSegment2 = new LineSegment();
                    Pair pair2 = points[2];
                    x = (double)pair2.First;
                    y = (double)pair2.Second;
                    lineSegment2.Point = new Point(x, -y + graph.Height);
                    pathFigure.Segments.Add(lineSegment2);

                    PathGeometry pathGeometry = new PathGeometry();
                    pathGeometry.Figures = new PathFigureCollection();
                    pathGeometry.Figures.Add(pathFigure);
                    path.Data = pathGeometry;

                    button1.ConnectedControls.Add(button2);
                    button2.ConnectedControls.Add(button1);
                    button1.ConnectionLines.Add(p);
                    button1.ConnectionLines.Add(path);
                    button2.ConnectionLines.Add(p);
                    button2.ConnectionLines.Add(path);
                    this.entityCanvas.Children.Add(p);
                    this.entityCanvas.Children.Add(path);

                    break;
                }
            }

            foreach (Edge e in graph.Edges)
            {
                if (e.Source.Equals(entity1.Name) && e.Target.Equals(entity2.Name))
                {
                    if (e.Lbl != null)
                    {
                        TextBlock text = new TextBlock();
                        text.Text = e.Lbl.Text;
                        text.TextAlignment = TextAlignment.Center;
                        text.HorizontalAlignment = HorizontalAlignment.Center;
                        text.VerticalAlignment = VerticalAlignment.Center;

                        Canvas.SetLeft(text, e.Lbl.X - e.Lbl.Width / 2 + 10);
                        Canvas.SetTop(text, -e.Lbl.Y + graph.Height - 10);

                        this.entityCanvas.Children.Add(text);
                    }
                    break;
                }
            }


        }
        #endregion

        #region "Get The File Size"
        private string GetFileSize(long length)
        {
            double bytes = (double)length;

            string fileSize = "0 KB";

            if (bytes >= 1073741824)
                fileSize = String.Format("{0:##.##}", bytes / 1073741824) + " GB";
            else if (bytes >= 1048576)
                fileSize = String.Format("{0:##.##}", bytes / 1048576) + " MB";
            else if (bytes >= 1024)
                fileSize = String.Format("{0:##.##}", bytes / 1024) + " KB";
            else if (bytes > 0 && bytes < 1024)
                fileSize = "1 KB";

            return fileSize;
        }
        #endregion

        #region "Show Error"

        private void ShowError()
        {
            MessageBox.Show("There was an error uploading your file");
            //TODO: call web service to try and find file and delete it.
        }

        #endregion
    }
}